/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.sbbs.jpa.entity.customtype.usertype;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.Objects;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.StringType;
import org.hibernate.usertype.UserType;
import org.jboss.logging.Logger;

/**
 *
 * @author Mr.fdc <fdc_messenger@126.com>
 */
public class StringSetUserType implements UserType {

    public static final StringSetUserType INSTANCE = new StringSetUserType();

    private static final Logger log = Logger.getLogger(StringSetUserType.class);
    private static final String DELIMITER = ",";

    @Override
    public int[] sqlTypes() {
        return new int[]{StringType.INSTANCE.sqlType()};
    }

    @Override
    public Class returnedClass() {
        return String.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return Objects.equals(x, y);
    }

    @Override
    public int hashCode(Object o) throws HibernateException {
        return Objects.hashCode(o);
    }

    private String[] fromString(String string) {
        if (string == null || string.isEmpty()) {
            return null;
        }
        String[] tokens = string.split(DELIMITER);
//        long[] values = new long[tokens.length];
//
//        for ( int i = 0; i < tokens.length; i++ ) {
//            values[i] = Long.valueOf( tokens[i], 2 );
//        }
        return tokens;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor ssci, Object o) throws HibernateException, SQLException {
        String columnName = names[0];
        String columnValue = (String) rs.getObject(columnName);
        log.debugv("Result set column {0} value is {1}", columnName, columnValue);
        return columnValue == null ? null
                : this.fromString(columnValue);
    }

    private String toString(String[] value) {
        StringBuilder builder = new StringBuilder();
        for (String token : value) {
            if (builder.length() > 0) {
                builder.append(DELIMITER);
            }
            builder.append(token);
        }
        return builder.toString();
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor ssci) throws HibernateException, SQLException {
        if (value == null) {
            log.debugv("Binding null to parameter {0} ", index);
            st.setNull(index, Types.VARCHAR);
        } else {
            String stringValue = this.toString((String[]) value);
            log.debugv("Binding {0} to parameter {1} ", stringValue, index);
            st.setString(index, stringValue);
        }
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        String[] os = (String[]) value;
        return value == null ? null : Arrays.copyOf(os, os.length + 1);
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object o) throws HibernateException {
        return (String[]) deepCopy(o);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

}
